package msrc

import (
	"context"
	"fmt"
	"net/http"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	hh "gitlab.systems-fd.com/packages/golang/helpers/h"
)

// httpService реализует базовый функционал HTTP сервера для приложений
// в виде сервиса для библиотеки.
type httpService struct {
	router http.Handler
	server *http.Server
	logger *logrus.Entry

	httpHost string
	httpPort string
}

// HttpService реализует фабрику сервиса для библиотеки, реализующего
// функционал HTTP сервера. На вход передаются обязательные аргументы
// для HTTP сервера, а так же возможна передача конфигурации запуска
// сервера.
//
// Если конфигурация запуска не передана, то будет использоваться конфигурация
// `http.Server{}`, при этом в обоих случаях (передано/не передано) будет
// происходить переопределение полей Addr и Handler в соответствии с
// переданными обязательными аргументами функции.
func HttpService(
	HttpHost, HttpPort string,
	Router http.Handler,
	GraceStopPeriodSeconds uint8,
	ServerConfig ...*http.Server,
) ServiceInterface {
	if 0 == len(ServerConfig) {
		ServerConfig = append(ServerConfig, &http.Server{})
	}

	if ServerConfig[0] == nil {
		ServerConfig[0] = &http.Server{}
	}

	return NewSimpleService(
		&httpService{
			router:   Router,
			server:   ServerConfig[0],
			logger:   logrus.WithField("prefix", "msrc/HttpService"),
			httpHost: HttpHost,
			httpPort: HttpPort,
		},
		GraceStopPeriodSeconds,
	)
}

// Run выполняет запуск упрощенной версии сервиса.
// На вход передается контекст, который передаст `<-ctx.Done()` в случае
// его остановки. Сам сервис должен обрабатывать его, если он не реализует
// своего собственного, отдельного алгоритма остановки.
func (h *httpService) Run(ctx context.Context) error {
	h.server.Addr = fmt.Sprintf(`%v:%v`, h.httpHost, h.httpPort)
	h.server.Handler = h.router

	h.logger.WithField("type", "tcp").WithField("host", h.server.Addr).Info("Starting HTTP Server")

	err := h.server.ListenAndServe()
	if hh.IsCtxDone(ctx) {
		return nil
	}

	if nil != err {
		h.logger.
			WithError(err).
			WithField("code", 500).
			WithField("host", h.server.Addr).
			Error(`Failed to serve HTTP requests`)

		return errors.Wrap(err, `[httpService]`)
	}

	return nil
}

// GracefulShutdown выполняет правильную остановку упрощенного
// сервиса. В данном случае, если сервис необходимо останавливать
// вызовом отдельного функционала остановки, этот метод предоставляет
// такую возможность.
//
// Метод вызывается в процессе остановки сервера и принимает на вход
// контекст с deadline, устанавливаемым сверху. За установленное время
// сервис либо завершает работу, либо отключается принудительно.
func (h *httpService) GracefulShutdown(ctx context.Context) error {
	return h.server.Shutdown(ctx)
}
